package aaa.schedule;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

import aaa.analyzer.ContainerData;
import aaa.analyzer.Feed;
import aaa.analyzer.FeedAnalyzer;
import aaa.database.Database;
import aaa.exception.AAAException;
import aaa.exception.AAAInternalException;
import aaa.exception.EmptyFieldException;
import aaa.exception.InvalidValueException;
import aaa.exception.RssRetrivalException;
import android.content.Context;
import android.database.Cursor;

/**
 *
 * @author 		Schnell Michaël, Zanette Sébastien
 */
public class Schedule implements Iterable<Lesson> {

	// HAS TO BE SORTED !
	public static final int[] VALID_PROJECT_IDS = new int[]{1, 2} ;

	private static final int NAME_MAX_LENGTH = 50 ;

	private static final String RESSOURCE_FORMAT  = "g?[0-9]+" ;
	private static final String RESSOURCES_FORMAT = 
			String.format("%1$s([ ]*,[ ]*%1$s)*", RESSOURCE_FORMAT) ;

	private static final String STRING_DISPLAY_FORMAT = "--------------------\nNom : '%s'\n" +
			"ProjectID : '%s'\nRessources : '%s'\nPériode : '%s'\nDer modif : '%s'\n" +
			"Notif : '%s'\nCrée le : '%s'\n--------------------" ;

	protected int projectId ;
	protected String name ;
	protected String ressources ;
	protected int period ;
	protected String lastModif ;
	protected int notify ;
	protected String created ;
	protected Lessons lessons ;

	/**
	 * 
	 * @param projectId
	 * @param name
	 * @param ressources
	 * @param period
	 * @param lastModif
	 * @param notify
	 * @param created
	 */
	public Schedule(int projectId, String name, String ressources, int period, 
			String lastModif, int notify, String created) {
		super() ;
		this.projectId = projectId;
		this.name = name.trim();
		this.ressources = ressources.trim() ;
		this.period = period;
		this.lastModif = lastModif;
		this.notify = notify;
		this.created = created;
		this.lessons = new Lessons() ;
	}

	public Schedule() {
		this(0, "", "", 0, "", 0, "") ;
	}

	public void setCreated(String created) {
		this.created = created;
	}

	public void setLastModif(String lastModif) {
		this.lastModif = lastModif;
	}

	public void setName(String name) {
		this.name = name;
	}

	public void setNotify(int notify) {
		this.notify = notify;
	}

	public void setPeriod(int period) {
		this.period = period;
	}

	public void setProjectId(int projectId) {
		this.projectId = projectId;
	}

	public void setRessources(String ressources) {
		this.ressources = ressources;
	}

	public String getCreated() {
		return this.created;
	}

	public String getLastModif() {
		return this.lastModif;
	}

	public String getName() {
		return this.name;
	}

	public int getNotify() {
		return this.notify;
	}

	public int getPeriod() {
		return this.period;
	}

	public int getProjectId() {
		return this.projectId;
	}

	/**
	 * 
	 * @return
	 */
	public String getRessources() {
		return this.ressources ;
	}// getRessources()

	/**
	 * 
	 */
	public String toString(){
		return String.format(Schedule.STRING_DISPLAY_FORMAT, this.name, this.projectId, 
				this.ressources, this.period, this.lastModif, this.notify, this.created) ;
	}// toString()

	/**
	 * 
	 */
	public void validate(Database db) throws AAAException {
		// ------------------------------------------------------------------------------------------------------
		// Check that no field is empty
		// ------------------------------------------------------------------------------------------------------
		if (this.name.equals("")) {
			throw new EmptyFieldException("Pas de nom donné pour l'emploi du temps.") ;
		}// if
		if (this.ressources.equals("")) {
			throw new EmptyFieldException("Pas de ressources données pour l'emploi du temps.") ;
		}// if
		if (this.created.equals("")) {
			throw new AAAInternalException("Date de création manquante.") ;
		}// if
		if (this.lastModif.equals("")) {
			throw new AAAInternalException("Date de dernière modification manquante.") ;
		}// if

		// ------------------------------------------------------------------------------------------------------
		// Check the given values
		// ------------------------------------------------------------------------------------------------------
		// The projectId can only have limited values
		if (Arrays.binarySearch(Schedule.VALID_PROJECT_IDS, this.projectId) < 0) {
			throw new InvalidValueException("Veuillez choisir un établissement valide.") ;
		}// if
		// The name has to be unique and is limited in size
		if (this.name.length() > NAME_MAX_LENGTH) {
			throw new InvalidValueException("Le nom pour l'emploi du temps est trop long.") ;
		}// if
		for(String name : db.getSchedulesName()) {
			if (name.equals(this.name)) {
				throw new InvalidValueException("Il existe déjà un emploi du temps avec ce nom.") ;
			}// if
		}// for
		// The ressources can only have the folloing format
		if (!this.ressources.matches(Schedule.RESSOURCES_FORMAT)) {
			throw new InvalidValueException("Les ressources doivent avoir l'un des formats suivants g suivi de chiffres ou juste des chiffres.") ;
		}// if
		// The period has to be positive
		if (this.period <= 0) {
			throw new InvalidValueException("La période doit être un nombre positif.") ;
		}// if
	}// validate()

	public void validateOnUpdate() throws AAAException {
		// ------------------------------------------------------------------------------------------------------
		// Check that no field is empty
		// ------------------------------------------------------------------------------------------------------
		if (this.ressources.equals("")) {
			throw new EmptyFieldException("Pas de ressources données pour l'emploi du temps.") ;
		}// if
		if (this.created.equals("")) {
			throw new AAAInternalException("Date de création manquante.") ;
		}// if
		if (this.lastModif.equals("")) {
			throw new AAAInternalException("Date de dernière modification manquante.") ;
		}// if

		// ------------------------------------------------------------------------------------------------------
		// Check the given values
		// ------------------------------------------------------------------------------------------------------
		// The projectId can only have limited values
		if (Arrays.binarySearch(Schedule.VALID_PROJECT_IDS, this.projectId) < 0) {
			throw new InvalidValueException("Veuillez choisir un établissement valide.") ;
		}// if
		// The ressources can only have the folloing format
		if (!this.ressources.matches(Schedule.RESSOURCES_FORMAT)) {
			throw new InvalidValueException("Les ressources doivent avoir l'un des formats suivants g suivi de chiffres ou juste des chiffres.") ;
		}// if
		// The period has to be positive
		if (this.period <= 0) {
			throw new InvalidValueException("La période doit être un nombre positif.") ;
		}// if
	}//validateOnUpdate()

	public void build(Context context) throws AAAException {
		// Retrieve rss feeds
		ArrayList<Feed> feeds = ContainerData.getFeeds(this.projectId, this.ressources, this.period) ;   
		// Analyse the feeds and build this instance of Schedule
		if (feeds == null) {
			throw new RssRetrivalException("Erreur lors de la récupération du flux.") ;
		}// if
		FeedAnalyzer fa = new FeedAnalyzer(this, feeds, new Database(context)) ;
		fa.start() ;
	}// build()

	public void build(Database db) {
		Cursor lessonsCursor = db.getLessonsCursor(this) ;
		this.ensureCapacity(lessonsCursor.getCount()) ;
		Lesson lesson = Database.cursorToLesson(lessonsCursor) ;
		while (lesson != null) {
			this.append(lesson) ;
			lesson = Database.cursorToLesson(lessonsCursor) ;
		}// while
		lessonsCursor.close() ;
		db.close() ;
	}// build()

	public void insert(Lesson l) {
		this.lessons.insert(l) ;
	}// add(Lesson)

	public void ensureCapacity(int size) {
		this.lessons.ensureCapacity(size) ;
	}// ensureCapacity(int) ;

	public Iterator<Lesson> iterator() {
		return new Iterator<Lesson>() {
			int next = 0 ;

			public boolean hasNext() {
				return (this.next < Schedule.this.lessons.size()) ;
			}// hasNext()

			public Lesson next() {
				return Schedule.this.get(this.next++) ;
			}// next()

			public void remove() {
				throw new UnsupportedOperationException("ScheduleIterator.remove n'a pas été implanté.") ;
			}
		} ;
	}// iterator()

	protected Lesson get(int i) {
		return this.lessons.get(i) ;
	}// get(int)

	public Lessons getLessons() {
		return this.lessons;
	}

	public void append(Lesson l) {
		this.lessons.append(l) ;
	}// append(Lesson)

	public int getNLessons() {
		return this.lessons.size() ;
	}// getNLessons()
	
	@Override
	public boolean equals(Object o) {
		if (o instanceof Schedule) {
			Schedule other = (Schedule) o ;
			return this.name.equals(other.name) && this.ressources.equals(other.ressources)
					&& (this.projectId == other.projectId) && (this.period == other.period) ;
		}// if
		return super.equals(o) ;
	}// equals(Object)

}// class Schedule
